#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <mtd/mtd-user.h>

#include "fpga_interface.h"

#define PROGRAM_NAME "fpga_interface"

static int fd1, fd2;

static int fpga_spi_fd;
static uint32_t fpga_spi_mode = 0;
static int8_t fpga_spi_bits = 8;
static uint32_t fpga_spi_speed = 5000000;

/**
 * @brief 写入数据到FPGA指定地址
 * 
 * @param page 页号
 * @param offset 偏移量
 * @return uint16_t 
 */
void spi_write_fpga(uint16_t page, size_t offset, uint16_t val)
{
    uint8_t tx[4];
    uint8_t rx[4];
    uint16_t addr;

    /* 读写接口参考 HB YSYZ FPGA 用户手册v1.5 */
    if (page > 4 || offset > 512)
        errmsg_die("invalid page or invalid offset");

    addr = 0x200 * page + offset;

    tx[0] = 0x28; // 00 101 000，101代表写操作
    tx[0] |= (addr >> 8) & 0xff;
    tx[1] = addr & 0xff;
    tx[2] = (val >> 8) & 0xff;
    tx[3] = val & 0xff;

    transfer(fpga_spi_fd, tx, rx, 4);
}

/**
 * @brief 从FPGA指定地址读出16位宽的数据
 * 
 * @param page 页号
 * @param offset 偏移量
 * @return uint16_t 
 */
uint16_t spi_read_fpga(uint16_t page, size_t offset)
{
    uint8_t tx[5];
    uint8_t rx[5];
    uint16_t addr, result;

    /* 读写接口参考 HB YSYZ FPGA 用户手册v1.5 */
    if (page > 4 || offset > 512)
        errmsg_die("invalid page or invalid offset");

    addr = 0x200 * page + offset;

    tx[0] = 0x20; // 00 100 000，100代表读操作
    tx[0] |= (addr >> 8) & 0xff;
    tx[1] = addr & 0xff;
    tx[2] = 0;
    tx[3] = 0;
    transfer(fpga_spi_fd, tx, rx, 5);

    // 去掉地址后2个bit的空白填充
    rx[2] = rx[2] << 2;
    rx[2] = (rx[3] >> 6) & 0xff | rx[2];
    rx[3] = rx[3] << 2;
    rx[3] = (rx[4] >> 6) & 0xff | rx[3];

    result = (rx[2] << 8) | rx[3];
    return result;
}

/**
 * @brief 初始化FPGA，打开FMC接口，初始化SPI
 * 
 * @return int 
 */
int fpga_init(uint16_t ch)
{
    // 通道1是/dev/mtd6
    // 通道2是/dev/mtd7
    if (ch == 0)
    {
    if ((fd1 = open("/dev/mtd6", O_SYNC | O_RDWR)) < 0)
        errmsg_die("open()");
    }
    else
    {
    if ((fd2 = open("/dev/mtd7", O_SYNC | O_RDWR)) < 0)
        errmsg_die("open()");
    }

    // 与FPGA通讯的SPI设备是：/dev/spidev2.0
    fpga_spi_fd = init_spi("/dev/spidev2.0");
    set_spi_mode(fpga_spi_fd, fpga_spi_mode, fpga_spi_bits, fpga_spi_speed);

    return 0;
}

int close_fpga(uint16_t ch)
{
    int ret;
    if (fpga_spi_fd != -1)
    {
        close_spi(fpga_spi_fd);
        fpga_spi_fd = -1;
    }
    if (ret < 0)
        errmsg_die("fpga spi close()");

    if (ch == 0)
    {
    ret = close(fd1);
    if (ret < 0)
        errmsg_die("fpga ch1 close()");
    }
    else
    {
    ret = close(fd2);
    if (ret < 0)
        errmsg_die("fpga ch2 close()");
    }
    return ret;
}

/**
 * @brief 读取FPGA DDR中的数据
 * 
 * @param len 长度
 * @param data 数据地址
 * @param ch 通道号
 * @return int 
 */
int read_fpga_ddr(uint32_t addr, size_t len, uint8_t *data, uint16_t ch)
{
    int err;
    int size = len * 32; // 长度以256bit为单位
    int fd;

    /* 寄存器定义参考 HB YSYZ FPGA 用户手册v1.5 */
    if (ch == 0)
    {
        fd = fd1;
        spi_write_fpga(2, 0x15, addr & 0xffff);
        spi_write_fpga(2, 0x16, addr >> 16);
        spi_write_fpga(2, 0x17, len & 0xffff);
        spi_write_fpga(2, 0x18, len >> 16);
        spi_write_fpga(2, 0x19, 0x1);
    }
    else
    {
        fd = fd2;
        spi_write_fpga(2, 0x1B, addr & 0xffff);
        spi_write_fpga(2, 0x1C, addr >> 16);
        spi_write_fpga(2, 0x1D, len & 0xffff);
        spi_write_fpga(2, 0x1E, len >> 16);
        spi_write_fpga(2, 0x1F, 0x1);
    }

    if (0 != lseek(fd, 0, SEEK_SET))
    {
        perror("lseek()");
        return 1;
    }
    err = read(fd, data, size);
    if (err < 0)
    {
        fprintf(stderr, "%s: read, size %#x\n", __func__, size);
        perror("read()");
    }
    else
    {
    }
    return err;
}

/**
 * @brief 往FPGA DDR中写入数据
 * 
 * @param len 长度
 * @param data 数据地址
 * @return int 
 */
int write_fpga_ddr(uint32_t addr, uint32_t len, uint8_t *data)
{
    int err;
    int size = len * 32; // 长度以256bit为单位

    /* 寄存器定义参考 HB YSYZ FPGA 用户手册v1.5 */
    spi_write_fpga(2, 0x21, 0x0);
    spi_write_fpga(2, 0x22, 0x0);
    spi_write_fpga(2, 0x23, len & 0xffff);
    spi_write_fpga(2, 0x24, len >> 16);
    spi_write_fpga(2, 0x25, 0x1);

    if (0 != lseek(fd1, 0, SEEK_SET))
    {
        perror("lseek()");
        return 1;
    }
    for (int i = 0; i < len; i++)
    {
        err = write(fd1, data + i * 32, 32); // 每个包的长度是256bit
    }
    if (err < 0)
    {
        fprintf(stderr, "%s: write, size %#x\n", __func__, 32);
        perror("write()");
        return 1;
    }
    else
    {
    }
    return err;
}

/**
 * @brief 设置DDR流控模式
 * 
 * @param val 0:正常模式 1：FMC读写测试模式
 * @return int 
 */
void set_flowctrl_mode(uint16_t val)
{
    spi_write_fpga(2, 0x2d, val);
}
